﻿using System.Collections.Generic;
using System.Windows.Controls;
using System.Windows;
using Newtonsoft.Json;
using System.IO;
using System.Linq;
using Renci.SshNet;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows.Data;
using System.Security.Cryptography;
using System.Text;
using System;
using PenguinPublishNS.Models;
using PenguinPublishNS.ViewModel;

namespace PenguinPublishNS.Views
{

    public partial class WebFilesList : UserControl
    {

        public ICollection<HostServer> comboResults;
        private ICollectionView myView;
        private string pPubPath = PenguinPublish.dataPath;
        private string localFilePath = PenguinPublish.webFilesPath;
        private HostServer curHost;
        private List<WebFile> listFileAndDirs = new List<WebFile>();
        //private static int portNumber = 22;  // optional
        //private static string hostName;
        private ConnectionInfo sftpConnInfo;
        IEnumerable<FileInfo> mySyncList = new List<FileInfo>();

        public WebFilesList()
        {
            InitializeComponent();
            if (!System.Net.NetworkInformation.NetworkInterface.GetIsNetworkAvailable())
            {
                titleLabel.Content = "No Network Connection! form disabled";
                titleLabel.Foreground = System.Windows.Media.Brushes.Red;
                txtSnapHashWarn.Visibility = Visibility.Collapsed;
                this.IsEnabled = false;
            }
            else
            {
                // comboResults = new Collection<HostServer>();
                var jsonString = File.ReadAllText(pPubPath + @"\dataHostSetting.json");
                comboResults = JsonConvert.DeserializeObject<List<HostServer>>(jsonString);
                // JsonConvert.PopulateObject(jsonString, comboResults);
                this.DataContext = comboResults;
                this.cboHostNames.ItemsSource = comboResults;
                myView = CollectionViewSource.GetDefaultView(comboResults);  // TODO: this is probably meant to be for Grid
                
                // curHost = (HostServer)myView.CurrentItem;   // since we fall out of the combobox selectionchanged event
                ReadFilesAndDirs(PenguinPublish.webFilesPath);  //must have in order to do a preview/compare
            }
        }

        void UserControl_Unloaded(object sender, RoutedEventArgs e)
        {
            // we can clean up in this event. Save files if necessary
        }

        private void cboHostNames_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            var hostID = ((ComboBox)sender).SelectedValue;
            if (myView != null)
            {
                myView.MoveCurrentToPosition(((ComboBox)sender).SelectedIndex);
                curHost = (HostServer)myView.CurrentItem;
                curHost.ipAvailable = sftpUtils.chkHostIp(curHost.HostAddress);
                BindingOperations.GetBindingExpression(chkValidIP, CheckBox.IsCheckedProperty).UpdateTarget();
                if (curHost.ipAvailable && curHost.sftpValid)
                {
                    panelButtons.IsEnabled = true;
                    sftpConnInfo = sftpUtils.initializeSFTP(curHost.HostAddress, curHost.username, curHost.usePPK, curHost.password, curHost.keyPassphrase, curHost.privateKey);
                    if (sftpUtils.checkFxs(sftpConnInfo, curHost.sitePath))
                    {
                        txtSnapHashWarn.Visibility = snapshotHashMatch();
                        btnPreview.IsEnabled = true;
                        btnPublish.IsEnabled = false;
                    }
                }
                else
                {
                    panelButtons.IsEnabled = false;
                }
            }

            try // hostID will be null as form is created
            {
                var streamR = new StreamReader(pPubPath + @"\" + hostID + @"\dataWebFile.json");
                var results = JsonConvert.DeserializeObject<List<WebFile>>(streamR.ReadToEnd());
                fileGrid.DataContext = results;
                fileGrid.ItemsSource = results;
                streamR.Close();
            }
            catch { }

        }


        private void btnPreview_Click(object sender, System.Windows.RoutedEventArgs e)
        {
            // start setting up the relay commands that will compare the file lists and set the Actions
            // step 1 find out if anything has actually changed by using an intersect. Compare is made on filehash and filepath
            previewIntersect();
            // step 2?
        }

        private void btnPublish_Click(object sender, System.Windows.RoutedEventArgs e)
        {
            SftpClient sftp = new SftpClient(sftpConnInfo);
            sftp.Connect();  // syncDir seems to work as advertised. if already there, does not publish again
            // it also seems to register the file diff and publishes if local file has changed
            // OOPS! No, it's just based on filename and length

            mySyncList = sftp.SynchronizeDirectories(localFilePath, curHost.sitePath, ".");
            fileGrid.DataContext = mySyncList;
            fileGrid.ItemsSource = mySyncList;
            // TODO: make conditional on success
            //trial just to test writing out file listing to .json in GUID dirs
            var fileDirObject = new WebFile();  //using class variable listFileAndDirs
            var hostID = cboHostNames.SelectedValue;
            ReadFilesAndDirs(PenguinPublish.webFilesPath);
            string json = JsonConvert.SerializeObject(listFileAndDirs, Formatting.Indented);
            File.WriteAllText(pPubPath + @"\" + hostID + @"\dataWebFile.json", json);
            curHost.snapshotHash = getMD5hash(pPubPath + @"\" + hostID + @"\dataWebFile.json");
            sftp.WriteAllText(curHost.sitePath + "/ppubhash.txt", curHost.snapshotHash);
            curHost.lastPublished = DateTime.Now;
            BindingOperations.GetBindingExpression(txtLastPublished, TextBlock.TextProperty).UpdateTarget();
            string json2 = JsonConvert.SerializeObject(comboResults, Formatting.Indented);
            File.WriteAllText(pPubPath + @"\dataHostSetting.json", json2);
        }

        private void btnClear_Click(object sender, RoutedEventArgs e)
        {
            // TODO: Clears the current snapshot stored in the .json file allowing for brand new Publish
            File.Delete(pPubPath + @"\" + curHost.hostId + @"\dataWebFile.json");
        }

        // utility function for reading file directory - Recursive
        protected void ReadFilesAndDirs(string dir)
        {
            foreach (string f in Directory.GetFiles(dir))
            {
                FileInfo finfo = new FileInfo(f);
                string fixPath = f.Replace(@"\", "/");
                var wf = new WebFile();
                wf.filepath = fixPath;
                wf.filehash = getMD5hash(f);
                wf.dateModified = finfo.LastWriteTime;
                listFileAndDirs.Add(wf);
            }
            foreach (string d in Directory.GetDirectories(dir))
            {
                string fixPath = d.Replace(@"\", "/");
                var wf = new WebFile();
                wf.filepath = fixPath;
                wf.filehash = d.GetHashCode().ToString();  //Cannot run ComputeHash, so just use this function
                listFileAndDirs.Add(wf);
                ReadFilesAndDirs(d);
            }
        }

        protected string getMD5hash(string fPath)
        {
            byte[] computedHash = new MD5CryptoServiceProvider().ComputeHash(File.ReadAllBytes(@fPath));
            var sBuilder = new StringBuilder();
            foreach (byte b in computedHash)
            {
                sBuilder.Append(b.ToString("x2").ToLower());
            }
            return sBuilder.ToString();
        }

        // utility functions for comparing things
        public void previewIntersect()
        {
            var hostID = cboHostNames.SelectedValue;
            var streamR = new StreamReader(pPubPath + @"\" + hostID + @"\dataWebFile.json");
            var results = JsonConvert.DeserializeObject<List<WebFile>>(streamR.ReadToEnd());
            IEnumerable<WebFile> myResults = listFileAndDirs.Intersect(results, new WebFileComparer());
            IEnumerable<WebFile> myDiff = listFileAndDirs.Except(results, new WebFileComparer());
            if (myDiff.Count() == 0)
                MessageBox.Show("There are no file to publish");
            else
                btnPublish.IsEnabled = true;

            fileGrid.ItemsSource = myDiff;

            //Debug.Print("Intersection gives " + myResults.Count().ToString());
            //Debug.Print("Count of files read in from the .json file " + listFileAndDirs.Count.ToString());
            //Debug.Print("Count of files NOW in the website directory " + results.Count.ToString());
            //Debug.Print("Difference gives: " + myDiff.Count().ToString());
            //foreach (WebFile d in myDiff)
            //    Debug.Print(d.filepath);
        }

        // utility function for reading in host snapshot hash value
        // not accounting for all outcomes yet. This file may be missing from host. TODO: Error handling
        public Visibility snapshotHashMatch()
        {
            string ppubhash = "";

            SftpClient sftp = new SftpClient(sftpConnInfo);
            sftp.ConnectionInfo.Timeout = TimeSpan.FromSeconds(2);  // this works and is global for all operations
            sftp.Connect();
            try
            {
                 ppubhash = sftp.ReadAllText(curHost.sitePath + "/ppubhash.txt");
            }
            catch (Exception ex)
            {
            }

            if (curHost.snapshotHash != ppubhash)
                return Visibility.Visible;
            else
                return Visibility.Collapsed;

        }

        private void cboHostNames_Loaded(object sender, RoutedEventArgs e)
        {
            cboHostNames_SelectionChanged(sender, null);
        }

    }
}
